package mac10.roles;

import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import de.dailab.jiactng.agentcore.ontology.IAgentDescription;
import mac10.bean.MasterBean;
import mac10.models.Cell;
import mac10.models.Cluster;
import mac10.models.ClusterBook;
import mac10.models.Point;
import mac10.models.WorldModel;
import mac10.monitor.InfoMonitor;
import mac10.util.AStarHeap;
import mac10.util.Node;

public class MasterShepherd {
	static public MasterShepherd staticMasterShepherd;
	HashMap<IAgentDescription, String> shepherds;

	public MasterShepherd(WorldModel worldmodel) {
		shepherds = new HashMap<IAgentDescription, String>();
	}

	public HashMap<IAgentDescription, String> currentShepherds(
			Map<IAgentDescription, Cell> allAgents, int step, int agentToRemain) {

		System.out.println("\tMasterShepherd.currentShepherds: allAgents.size=" + allAgents.size());
		
		// if(step % 3 == 0)
		ClusterBook.staticClusterBook.update();

		HashMap<String, Cluster> book = ClusterBook.staticClusterBook.getBook();

		Map<IAgentDescription, Cell> possibleAgents = new HashMap<IAgentDescription, Cell>(
				allAgents);

		for (Iterator<IAgentDescription> iterator = shepherds.keySet()
				.iterator(); iterator.hasNext();) {
			
			IAgentDescription agent = iterator.next();

			// entferne nicht erlaubte agents
			if (!possibleAgents.containsKey(agent)) {
				iterator.remove();
				continue;
			}

			// der cluster ist nicht mehr bekannt
			if (!book.keySet().contains(shepherds.get(agent))
					|| book.get(shepherds.get(agent)).isInCorral())// clustercenter
																	// im corral
			{
				iterator.remove();
			} else {
				possibleAgents.remove(agent);
				book.get(shepherds.get(agent)).shepherds.put(agent, allAgents
						.get(agent));
			}
		}

		Cluster[] clusterArray = new Cluster[book.size()];
		clusterArray = book.values().toArray(clusterArray);

		Arrays.sort(clusterArray, new Comparator<Cluster>() {
			public int compare(Cluster a, Cluster b) {
//				if (a.getStart() == null || b.getStart() == null) {
//					return 0;
//				}
				
				if (b.isEnemyCorral()) {
					return Integer.MAX_VALUE;
				} else if (a.isEnemyCorral()) {
					return Integer.MIN_VALUE;
				}
				
				if(a.pathToCorral == null) {
				  return Integer.MAX_VALUE;
				} else if(b.pathToCorral == null) {
				  return Integer.MIN_VALUE;
				}
				
				return a.getPathLengthToCorral() - b.getPathLengthToCorral();
			}
		});
		
		//START prioritize nearest herd to corral
//		int minDistance = Integer.MAX_VALUE;
//		int index = 0;
//		
//		for (int c = 0; c < clusterArray.length; c++) {
//			Cluster cluster = clusterArray[c];
//			if (cluster.getPathLengthToCorral() > 0) {
//				if (cluster.getPathLengthToCorral() < minDistance) {
//					minDistance = cluster.getPathLengthToCorral();
//					index = c;
//				}
//			}
//		}
//		
//		if(clusterArray.length > 0) {
//			Cluster help = clusterArray[0];
//			clusterArray[0] = clusterArray[index];
//			clusterArray[index] = help;
//		}
		//END prioritize nearest herd to corral
		
		for (int i = 0; i < clusterArray.length; i++) {

			Cluster cluster = clusterArray[i];
//			System.out.println("\t\tCluster " + cluster.getId() + " size=" + cluster.getPathLengthToCorral());
			
			WorldModel.staticWorldModel.getCell(cluster.getCenter()).setMarked(
					Cell.BLUE_CIRCLE);

			if (cluster.isInCorral()) {
				continue;
			}
			int c = countAgentsForCluster(cluster.getId());

			int needed = cluster.shepherdsNeeded();

			if (c == needed) {
				continue;
			} 
			
			else if (c > needed) {
				int rest = c - needed;
				removeAgentsFromCluster(rest, cluster, cluster.getId(),
						possibleAgents, allAgents);
			}

			else if (!possibleAgents.isEmpty()
					&& possibleAgents.size() >= agentToRemain) {
				int rest = needed - c;
				addShepherdsToCluster(rest, cluster, cluster.getId(),
						possibleAgents);
			}
		}

		debugInfo();
		StringBuffer sb = new StringBuffer();
		for (Cluster c:clusterArray) {
			sb.append("Cluster: ");
			sb.append(c.getId());
			sb.append(" - ");
			for(IAgentDescription iad : c.shepherds.keySet()) {
			  sb.append(iad.getName());
			  sb.append(" ");
			}
			sb.append("\n");
		}
		System.out.println(sb.toString());
		return shepherds;
	}

	private void removeAgentsFromCluster(int rest, Cluster cluster,
			String hash, Map<IAgentDescription, Cell> agents,
			Map<IAgentDescription, Cell> allAgents) {

		System.out.println("\tMasterShepherd.removeAgentsFromCluster: agents.size=" + agents.size());
		
		for (Iterator<IAgentDescription> iterator2 = shepherds.keySet()
				.iterator(); iterator2.hasNext();) {
			IAgentDescription agent = (IAgentDescription) iterator2.next();

			if (shepherds.get(agent).equals(hash)) {
				iterator2.remove();
				cluster.shepherds.remove(agent);
				agents.put(agent, allAgents.get(agent));

			}
			rest--;

			if (rest <= 0)
				return;
		}

	}

	private void addShepherdsToCluster(int rest, Cluster cluster, String hash,
			Map<IAgentDescription, Cell> agents) {
		
		System.out.println("\tMasterShepherd.addShepherdsToCluster: agents.size=" + agents.size());

		if (agents.size() == 0) {
			System.err
					.println("\tMasterShepherd.addShepherdsToCluster: NO AGENTS");
			return;
		}

		while (rest > 0) {
			rest--;

			if (agents.isEmpty())
				break;

			IAgentDescription shepherd = findNearestAgent(cluster.getCenter(),
					agents);

			if (shepherd == null) {
				System.err
						.println("MasterShepherd.addShepherdsToCluster: shepherd ist null "
								+ agents.size());
				break;
			} else {
				cluster.shepherds.put(shepherd, agents.get(shepherd));
				agents.remove(shepherd);
				shepherds.put(shepherd, hash);

			}

		}
	}

	public int countAgentsForCluster(String hash) {
		int c = 0;

		for (String string : shepherds.values()) {
			if (string.equals(hash))
				c++;
		}

		return c;
	}

	private IAgentDescription findNearestAgent(Point clusterCenter,
			Map<IAgentDescription, Cell> agents) {

		if (agents.isEmpty()) {
			System.out.println("jo agents leer sollte net sein");
			return null;
		}

		int minDistance = Integer.MAX_VALUE, distance;
		final Cell goalCell = new Cell(clusterCenter.getX(), clusterCenter
				.getY(), -1);
		Cell nearestCell = null;
		Cell agentPostions[] = agents.values().toArray(new Cell[agents.size()]);
		Arrays.sort(agentPostions, new Comparator<Cell>() {
			public int compare(Cell a, Cell b) {
				int distA = a.maxNorm(goalCell);
				int distB = b.maxNorm(goalCell);
				if (distA == distB)
					return 0;
				else if (distA > distB)
					return 1;
				else
					return -1;
			}
		});

		int firstMaxNorm = WorldModel.getDistance(clusterCenter,
				agentPostions[0]);

		for (int i = 0; i < agentPostions.length; i++) {
			// System.err.println("\t teste " + agentPostions[i] + " "+button);
			distance = WorldModel.getDistance(clusterCenter, agentPostions[i]);

			if (minDistance <= distance) {
				// es gibt keinen besseren switcher
				break;
			}

			Node goal = existsPath(agentPostions[i],
					clusterCenter, // null vllt
					MasterBean.getShortestDeadline() - 300, false, false, false,
					false);

			if (goal == null) {
				System.out.println("NULLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL");
				continue;
			}

			if (goal.getF() < minDistance) {
				minDistance = goal.getF();
				nearestCell = agentPostions[i];

				if (minDistance < 2 * firstMaxNorm)
					break;

				if (MasterBean.getShortestDeadline() - 400 <= System
						.currentTimeMillis())
					break;
			}

		}
		for (IAgentDescription agent : agents.keySet()) {
			if (agents.get(agent).equals(nearestCell))
				return agent;
		}

		return null;
	}

	private Node existsPath(Point start, Point goal, long deadline,
			boolean avoidAgents, boolean avoidCows, boolean avoidOpenFences,
			boolean avoidClosedFences) {
		AStarHeap astar = new AStarHeap(WorldModel.staticWorldModel, start,
				goal);
		astar.setAvoidAgents(avoidAgents);
		astar.setAvoidCows(avoidCows);
		astar.setAvoidOpenFences(avoidOpenFences);
		astar.setAvoidClosedFences(avoidClosedFences);
		Node to = astar.AStar(deadline);
		return to;
	}

	public void debugInfo() {
		InfoMonitor.staticMonitor.setRightText("");

		for (String hash : ClusterBook.staticClusterBook.getBook().keySet()) {
			String s = hash + " : ";
			if (ClusterBook.staticClusterBook.getBook().get(hash).isInCorral())
				s += "corral";
			else
				for (IAgentDescription agent : shepherds.keySet()) {
					if (shepherds.get(agent).equals(hash))
						s += agent.getName() + " ";
				}
			InfoMonitor.staticMonitor.addLineToRightText(s);
		}
		InfoMonitor.staticMonitor.addLineToRightText("----------------\n");
		ClusterBook.staticClusterBook.clusterDebug();

	}

}
